#include "pch.h"
#include <windows.h>

using namespace std;
using namespace concurrency;

using namespace Platform;
using namespace Windows::UI::Core;
using namespace Windows::ApplicationModel::Core;


template <typename T>
T WaitForTask(task<T> t,  bool& success, DWORD timeout = 0)
{
    CoreDispatcher^ dispatcher = CoreApplication::MainView->CoreWindow->Dispatcher;
    HANDLE hEvent = CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);

    if (hEvent == NULL)
        throw std::bad_alloc();

    T result;
    bool exceptionHandled = false;
    wstring exceptionMessage;

	t.then([&hEvent, &exceptionHandled, &exceptionMessage, &result](task<T> previousTask)
	{
        try
        {
            result = previousTask.get();
        } 
        catch (Platform::Exception^ ex)
        {
            exceptionHandled = true;
            exceptionMessage = std::wstring(ex->Message->Data());
        }
        catch (std::exception & ex)
        {
            exceptionHandled = true;
            exceptionMessage = std::wstring(ex.what(), ex.what() + strlen(ex.what()));
        }
        catch (...)
        {
            exceptionHandled = true;
            exceptionMessage = std::wstring(L"Unrecognized C++ exception.");
        }

        SetEvent(hEvent);

    }, task_continuation_context::use_arbitrary());

 

    DWORD waitResult = STATUS_PENDING;
	ULONGLONG startMS = GetTickCount64();
	ULONGLONG currentMS = 0;
    while (waitResult != WAIT_OBJECT_0 &&  (timeout == 0 || currentMS < timeout))
    {
        dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
        waitResult = WaitForSingleObjectEx(hEvent, 0, TRUE);
		currentMS = GetTickCount64() - startMS;
    }

	success = waitResult == WAIT_OBJECT_0;



    CloseHandle(hEvent);

    if (exceptionHandled)
	{
		throw ref new FailureException(ref new String(exceptionMessage.c_str()));
		//        Microsoft::VisualStudio::CppUnitTestFramework::Assert::Fail(exceptionMessage.c_str());
	}
    return result;
}

 

template<>
inline void WaitForTask<void>(task<void> t, bool& success, DWORD timeout)
{
    WaitForTask(t.then([]{ return true; }), success, timeout);
}


template <typename T>
inline T WaitForTask(Windows::Foundation::IAsyncOperation<T>^ t, bool& success, DWORD timeout)
{
    return WaitForTask(task<T>(t), success, timeout);
} 

inline void WaitForTask(Windows::Foundation::IAsyncAction^ t, bool& success, DWORD timeout)
{
	return WaitForTask(task<void>(t), success, timeout);
}